package com.gitee.ed.pattern.singleton;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import com.gitee.ed.structure.map.ClassMap;

import java.util.*;

/**
 * 对象容器
 * 单例模式的外部实现
 *
 * @author hongda.li
 */
public final class BeanContainer {
    /**
     * 所有单例的容器
     */
    private static final ClassMap CONTAINER = new ClassMap();

    /**
     * 私有构造器
     */
    private BeanContainer() {
    }

    /**
     * 将容器中所有尚未实例化的对象进行实例化操作
     */
    public static void instantiate() {
        CONTAINER.entrySet()
                .stream()
                .filter(entry -> entry.getValue() == null)
                .map(Map.Entry::getKey)
                .map(BeanCreator::new)
                .toList()
                .forEach(BeanCreator::create);

        CONTAINER.values().removeIf(Objects::isNull);
    }

    /**
     * 注册一个对象实例
     *
     * @param bean 对象实例
     */
    public static void register(Object bean) {
        BeanContainer.register(bean.getClass(), bean);
    }

    /**
     * 注册一个尚未实例化的对象
     * 此注册方法在调用 instantiate() 方法前不会主动实例化
     *
     * @param toInstantiated 对象类型
     */
    public static void register(Class<?> toInstantiated) {
        BeanContainer.register(toInstantiated, null);
    }

    /**
     * 注册一个对象实例
     * 此方法会检查对象类型与对象的关系，不匹配的类型会抛出异常
     *
     * @param toInstantiated 对象的类型
     * @param bean           对象实例
     */
    public static void register(Class<?> toInstantiated, Object bean) {
        Objects.requireNonNull(toInstantiated);
        Assert.isTrue(bean == null || toInstantiated.isInstance(bean));
        CONTAINER.put(toInstantiated, bean);
    }

    /**
     * 查询指定类型的首个实例
     * 若不存在任何实例则尝试实例化
     *
     * @param beanType 指定类型
     * @param <Bean>   对象泛型
     * @return 匹配的实例
     */
    public static <Bean> Bean findFirst(Class<Bean> beanType) {
        Objects.requireNonNull(beanType);
        return ClassUtil.isNormalClass(beanType)
                ? CONTAINER.getInstance(beanType)
                : CollUtil.getFirst(BeanContainer.find(beanType));
    }

    /**
     * 查询指定类型的所有实例
     * 该类型可以是实例的父类或父接口
     * 该类型可能不存在于容器中
     *
     * @param beanType 指定类型
     * @param <Bean>   对象泛型
     * @return 匹配的所有实例
     */
    public static <Bean> List<Bean> find(Class<Bean> beanType) {
        Objects.requireNonNull(beanType);
        return ClassUtil.isNormalClass(beanType)
                ? Collections.singletonList(CONTAINER.getInstance(beanType))
                : CONTAINER.entrySet()
                .stream()
                .filter(entry -> beanType.isAssignableFrom(entry.getKey()))
                .map(Map.Entry::getValue)
                .map(beanType::cast)
                .toList();
    }

    /**
     * 获取单例容器
     *
     * @return 单例容器
     */
    public static ClassMap getContainer() {
        return new ClassMap(CONTAINER);
    }

    /**
     * 获取所有的单例对象类型
     *
     * @return 单例对象类型集合
     */
    public static Set<Class<?>> getBeanTypes() {
        return new HashSet<>(CONTAINER.keySet());
    }
}
